import LinkedList, { Node } from "./LinkedList";

export class DoubleNode<T> {
  prev: DoubleNode<T> | null = null;
  next: DoubleNode<T> | null = null;
  value: T;
  constructor(value: T) {
    this.value = value;
    this.prev = null;
    this.next = null;
  }
}

export default class DoubleList<T> extends LinkedList<T> {
  protected _tail: DoubleNode<T> | null = null;
  protected _head: DoubleNode<T> | null = null;
  constructor() {
    super();
    this._tail = null;
    this._head = null;
  }

  push(val: T) {
    const node = new DoubleNode(val);
    if (this.isEmpty()) {
      this._head = node;
      this._tail = node;
    } else {
      const tail = this._tail!;
      tail.next = node;
      node.prev = tail;
      this._tail = node;
    }
    this._size++;
  }

  insert(val: T, index: number): boolean {
    if (this._isSafeIndex(index, true)) {
      return false;
    }

    const node = new DoubleNode(val);
    let current: DoubleNode<T> | null = null;
    if (index === 0) {
      if (this._head === null) {
        this._head = node;
        this._tail = node;
      } else {
        current = this._head;
        node.next = current;
        current.prev = node;
        this._head = node;
      }
    } else if (this._size === index) {
      current = this._tail!;
      current.next = node;
      node.prev = current;
      this._tail = node;
    } else {
      const prev = this.getNodeAt(index - 1)! as DoubleNode<T>;
      current = prev.next as DoubleNode<T>;
      prev.next = node;
      node.prev = prev;
      node.next = current;
      current.prev = node;
    }
    this._size++;
    return true;
  }

  removeAt(index: number) {
    if (!this._isSafeIndex(index)) {
      return;
    }

    let current;
    if (index === 0) {
      current = this._head!;
      this._head = current.next as DoubleNode<T>;
      if (this._size === 1) {
        this._tail = null;
      } else {
        this._head.prev = null;
      }
    } else if (this._size - 1 === index) {
      current = this._tail!;
      this._tail = current.prev!;
      this._tail.next = null;
    } else {
      const prev = this.getNodeAt(index - 1)! as DoubleNode<T>;
      current = prev.next!;
      prev.next = current?.next!;
      current.next!.prev = prev;
    }
    this._size--;
  }

  getTail() {
    return this._tail;
  }

  reverseToString() {
    let str = "";
    if (this.isEmpty()) {
      return str;
    }
    let current = this._tail;
    str = `${current?.value}`;
    while (current && current.prev !== null) {
      current = current.prev;
      str += `,${current.value}`;
    }
    return str;
  }
}
